package com.xxxtea.security;

import cn.hutool.core.lang.ConsoleTable;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.xxxtea.common.Constants;
import com.xxxtea.common.execption.NoAuthException;
import com.xxxtea.component.LocalCache;
import com.xxxtea.entity.User;
import com.xxxtea.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 认证管理器
 * 
 * 负责用户认证的核心管理类，包括：
 * - 用户登录和登出
 * - Token的生成、验证和管理
 * - 用户身份验证
 * - 默认用户和模拟用户支持
 * 
 * 实现ApplicationRunner接口，在应用启动时输出登录信息
 * 
 * @author badman
 * @since 1.0.0
 */
@Slf4j
@Component
public class AuthManager implements ApplicationRunner {
	
	/**
	 * Token映射表，存储token到用户ID的映射关系
	 * key: token字符串
	 * value: 用户ID
	 */
	private Map<String, Integer> tokenMap;
	
	/**
	 * 本地缓存组件，用于持久化token映射
	 */
	@Resource
	private LocalCache localCache;
	
	/**
	 * 用户服务，用于查询用户信息
	 */
	@Resource
	private UserService userService;
	
	/**
	 * 认证配置属性
	 */
	@Resource
	private AuthProperties authProperties;

	/**
	 * 初始化方法
	 * 
	 * 在Bean创建后调用，初始化token映射表并加载默认用户
	 */
	@PostConstruct
	public void init() {
		// 从本地缓存中获取token映射表，如果不存在则创建新的
		this.tokenMap = localCache.getIfAbsent(Constants.TOKEN_KEY, ConcurrentHashMap::new);
		
		// 加载默认用户配置
		if (authProperties.getDefaultUsers() != null) {
			for (User user : authProperties.getDefaultUsers()) {
				tokenMap.put(user.getToken(), user.getId());
			}
		}
	}

	/**
	 * 应用启动后的回调方法
	 * 
	 * 在应用启动完成后输出当前所有登录用户的信息
	 * 
	 * @param args 应用启动参数
	 */
	@Override
	public void run(ApplicationArguments args) {
		this.logTokenInfo();
	}

	/**
	 * 输出Token信息到控制台
	 * 
	 * 以表格形式展示当前所有登录用户的信息，包括ID、昵称、角色和Token
	 */
	public void logTokenInfo() {
		ConsoleTable consoleTable = ConsoleTable.create().addHeader("id", "name", "role", "token");
		for (String token : tokenMap.keySet()) {
			try {
				User user = verify(token);
				consoleTable.addBody(String.valueOf(user.getId()), user.getNickname(), user.getRole().getLabel(), user.getToken());
			} catch (Exception ignored) {
				// 忽略验证失败的用户，不输出错误信息
			}
		}
		log.info("登录信息: \n{}", consoleTable);
	}

	/**
	 * 添加用户登录信息
	 * 
	 * 为用户生成或分配Token，并存储到token映射表中
	 * 如果是默认用户，使用预配置的Token；否则生成新的UUID作为Token
	 * 
	 * @param user 用户对象
	 * @return 用户的Token字符串
	 */
	public String add(User user) {
		List<User> defaultUsers = authProperties.getDefaultUsers();
		if (defaultUsers != null) {
			// 检查是否为默认用户
			Optional<User> optional = defaultUsers.stream().filter(e -> e.getId().equals(user.getId())).findAny();
			if (optional.isPresent()) {
				log.info("使用临时令牌 userId: {}, token: {}", user.getId(), user.getToken());
				String token = optional.get().getToken();
				user.setToken(token);
				return token;
			}
		}
		
		// 移除用户之前的登录信息
		this.remove(user);
		
		// 生成新的UUID作为Token
		String token = IdUtil.fastUUID();
		tokenMap.put(token, user.getId());
		user.setToken(token);
		return token;
	}

	/**
	 * 移除用户登录信息
	 * 
	 * 从token映射表中移除指定用户的所有Token
	 * 
	 * @param user 用户对象
	 */
	public void remove(User user) {
		for (String token : tokenMap.keySet()) {
			Integer userId = tokenMap.get(token);
			if (Objects.equals(userId, user.getId())) {
				tokenMap.remove(token);
				return;
			}
		}
	}

	/**
	 * 验证HTTP请求中的用户身份
	 * 
	 * 从请求头或请求参数中提取Token，并验证用户身份
	 * 如果验证失败且配置了模拟用户，则返回模拟用户
	 * 
	 * @param request HTTP请求对象
	 * @return 验证通过的用户对象
	 * @throws NoAuthException 认证失败时抛出异常
	 */
	public User verify(HttpServletRequest request) throws NoAuthException {
		// 从请求头或请求参数中获取Token
		String token = StrUtil.firstNonBlank(
				request.getHeader(Constants.TOKEN_KEY),
				request.getParameter(Constants.TOKEN_KEY)
		);
		try {
			return verify(token);
		} catch (NoAuthException e) {
			// 如果验证失败且配置了模拟用户，返回模拟用户
			if (authProperties.getMockUser() != null) {
				return authProperties.getMockUser();
			}
			throw e;
		}
	}

	/**
	 * 验证Token并返回用户信息
	 * 
	 * 根据Token查找用户ID，然后查询完整的用户信息
	 * 
	 * @param token 用户Token
	 * @return 验证通过的用户对象
	 * @throws NoAuthException Token无效或用户不存在时抛出异常
	 */
	public User verify(String token) throws NoAuthException {
		// 检查Token是否为空
		if (StrUtil.isBlank(token)) {
			throw new NoAuthException("用户未登录");
		}
		
		// 根据Token获取用户ID
		Integer userId = tokenMap.get(token);
		if (userId == null) {
			throw new NoAuthException("用户未登录");
		}
		
		// 查询用户详细信息
		User user = userService.getById(userId);
		if (user == null) {
			throw new NoAuthException("用户信息不存在");
		}
		
		// 设置Token到用户对象中
		user.setToken(token);
		return user;
	}
}
